package org.dromara.device.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.seata.spring.annotation.GlobalTransactional;
import jodd.time.TimeUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.dubbo.config.annotation.DubboReference;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.tenant.helper.TenantHelper;
import org.dromara.device.api.domain.vo.OrdinaryDeviceVo;
import org.dromara.device.domain.DeviceConfig;
import org.dromara.device.domain.DeviceStoreArea;
import org.dromara.device.domain.bo.DeviceBo;
import org.dromara.device.domain.bo.DeviceConfigBo;
import org.dromara.device.domain.bo.DeviceInfoBo;
import org.dromara.device.domain.vo.DeviceConfigVo;
import org.dromara.device.domain.vo.DeviceInfoVo;
import org.dromara.device.domain.vo.DeviceVo;
import org.dromara.device.mapper.DeviceConfigMapper;
import org.dromara.device.mapper.DeviceStoreAreaMapper;
import org.dromara.device.service.IDeviceConfigService;
import org.dromara.system.api.RemoteStoreService;
import org.dromara.system.api.RemoteUserService;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 设备管理-设备配置信息Service业务层处理
 *
 * @author samuel
 * @date 2024-03-21
 */
@Log4j2
@Service
@RequiredArgsConstructor
public class DeviceConfigServiceImpl implements IDeviceConfigService {

    private final DeviceConfigMapper baseMapper;
    private final DeviceStoreAreaMapper deviceStoreAreaMapper;
    @DubboReference
    private RemoteStoreService remoteStoreService;
    @DubboReference
    private RemoteUserService remoteUserService;


    /**
     * 查询设备管理-设备配置信息
     */
    @Override
    public DeviceConfigVo queryById(Long deviceId){
        DeviceConfigVo deviceConfigVo = baseMapper.selectVoById(deviceId);
        if (ObjUtil.isNull(deviceConfigVo)) {
            return deviceConfigVo;
        }

        List<Long> storeIdList = CollUtil.newArrayList(deviceConfigVo.getStoreId());
        Map<Long, String> storeMap = remoteStoreService.selectNamesByStoreList(storeIdList);
        if (MapUtil.isEmpty(storeMap)) {
            return deviceConfigVo;
        }
        deviceConfigVo.setStoreName(storeMap.get(deviceConfigVo.getStoreId()));
        return deviceConfigVo;
    }

    /**
     * 查询设备管理-设备配置信息列表
     */
    @Override
    public TableDataInfo<DeviceConfigVo> queryPageList(DeviceConfigBo bo, PageQuery pageQuery) {
        IPage<DeviceConfigVo> result = TenantHelper.ignore(() -> {
            LambdaQueryWrapper<DeviceConfig> lqw = buildQueryWrapper(bo);
            return baseMapper.selectVoPage(pageQuery.build(), lqw);
        });

        List<DeviceConfigVo> rows = result.getRecords();
        if (CollUtil.isEmpty(rows)) {
            return TableDataInfo.build(result);
        }

        buildStoreName(rows);
        result.setRecords(rows);
        return TableDataInfo.build(result);
    }

    /**
     * 查询设备管理-设备配置信息列表
     */
    @Override
    public List<DeviceConfigVo> queryList(DeviceConfigBo bo) {
        List<DeviceConfigVo> rows = TenantHelper.ignore(() -> {
            LambdaQueryWrapper<DeviceConfig> lqw = buildQueryWrapper(bo);
            return baseMapper.selectVoList(lqw);
        });

        if (CollUtil.isEmpty(rows)) {
            return rows;
        }
        buildStoreName(rows);
        return rows;
    }

    private void buildStoreName(List<DeviceConfigVo> rows) {
        List<Long> storeIdList = new ArrayList<>(rows.stream().map(DeviceConfigVo::getStoreId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet()));
        Map<Long, String> storeMap = remoteStoreService.selectNamesByStoreList(storeIdList);
        if (MapUtil.isNotEmpty(storeMap)) {
            rows.forEach(row -> row.setStoreName(storeMap.get(row.getStoreId())));
        }
    }

    private LambdaQueryWrapper<DeviceConfig> buildQueryWrapper(DeviceConfigBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<DeviceConfig> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getTenantId()), DeviceConfig::getTenantId, bo.getTenantId());
        lqw.like(StringUtils.isNotBlank(bo.getDeviceSn()), DeviceConfig::getDeviceSn, bo.getDeviceSn());
        lqw.eq(StringUtils.isNotBlank(bo.getAppId()), DeviceConfig::getAppId, bo.getAppId());
        lqw.eq(StringUtils.isNotBlank(bo.getAppSecret()), DeviceConfig::getAppSecret, bo.getAppSecret());
        lqw.eq(StringUtils.isNotBlank(bo.getPublicKey()), DeviceConfig::getPublicKey, bo.getPublicKey());
        lqw.eq(StringUtils.isNotBlank(bo.getPrivateKey()), DeviceConfig::getPrivateKey, bo.getPrivateKey());
        lqw.eq(StringUtils.isNotBlank(bo.getClientType()), DeviceConfig::getClientType, bo.getClientType());
        lqw.eq(StringUtils.isNotBlank(bo.getApiVersion()), DeviceConfig::getApiVersion, bo.getApiVersion());
        lqw.like(StringUtils.isNotBlank(bo.getPhone()), DeviceConfig::getPhone, bo.getPhone());
        lqw.like(StringUtils.isNotBlank(bo.getName()), DeviceConfig::getName, bo.getName());
        lqw.eq(StringUtils.isNotBlank(bo.getCallBackUrl()), DeviceConfig::getCallBackUrl, bo.getCallBackUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getType()), DeviceConfig::getType, bo.getType());
        lqw.eq(StringUtils.isNotBlank(bo.getSoftVersion()), DeviceConfig::getSoftVersion, bo.getSoftVersion());
        lqw.eq(StringUtils.isNotBlank(bo.getIp()), DeviceConfig::getIp, bo.getIp());
        lqw.eq(bo.getPort() != null, DeviceConfig::getPort, bo.getPort());
        lqw.eq(StringUtils.isNotBlank(bo.getPublicIp()), DeviceConfig::getPublicIp, bo.getPublicIp());
        lqw.eq(bo.getPublicPort() != null, DeviceConfig::getPublicPort, bo.getPublicPort());
        lqw.eq(StringUtils.isNotBlank(bo.getUrl()), DeviceConfig::getUrl, bo.getUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getReplayHost()), DeviceConfig::getReplayHost, bo.getReplayHost());
        lqw.eq(bo.getReplayPort() != null, DeviceConfig::getReplayPort, bo.getReplayPort());
        lqw.eq(StringUtils.isNotBlank(bo.getLoginKey()), DeviceConfig::getLoginKey, bo.getLoginKey());
        lqw.eq(StringUtils.isNotBlank(bo.getModel()), DeviceConfig::getModel, bo.getModel());
        lqw.eq(StringUtils.isNotBlank(bo.getDisplay()), DeviceConfig::getDisplay, bo.getDisplay());
        lqw.eq(StringUtils.isNotBlank(bo.getControl()), DeviceConfig::getControl, bo.getControl());
        lqw.eq(bo.getBingTime() != null, DeviceConfig::getBingTime, bo.getBingTime());
        lqw.eq(StringUtils.isNotBlank(bo.getDeviceArea()), DeviceConfig::getDeviceArea, bo.getDeviceArea());
        lqw.eq(bo.getDeviceCapabilityId() != null, DeviceConfig::getDeviceCapabilityId, bo.getDeviceCapabilityId());
        lqw.eq(StringUtils.isNotBlank(bo.getManufacturerCode()), DeviceConfig::getManufacturerCode, bo.getManufacturerCode());
        lqw.eq(StringUtils.isNotBlank(bo.getHotspotBgImg()), DeviceConfig::getHotspotBgImg, bo.getHotspotBgImg());
        lqw.eq(StringUtils.isNotBlank(bo.getPosition()), DeviceConfig::getPosition, bo.getPosition());
        lqw.eq(StringUtils.isNotBlank(bo.getMediaSource()), DeviceConfig::getMediaSource, bo.getMediaSource());
        lqw.eq(StringUtils.isNotBlank(bo.getPlayUrl()), DeviceConfig::getPlayUrl, bo.getPlayUrl());
        lqw.eq(StringUtils.isNotBlank(bo.getInOutDirection()), DeviceConfig::getInOutDirection, bo.getInOutDirection());
        lqw.eq(StringUtils.isNotBlank(bo.getExtendDeviceRel()), DeviceConfig::getExtendDeviceRel, bo.getExtendDeviceRel());
        lqw.eq(bo.getStoreId() != null, DeviceConfig::getStoreId, bo.getStoreId());
        lqw.eq(StringUtils.isNotBlank(bo.getArgs()), DeviceConfig::getArgs, bo.getArgs());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), DeviceConfig::getStatus, bo.getStatus());
        lqw.eq(bo.getLastOnlineTime() != null, DeviceConfig::getLastOnlineTime, bo.getLastOnlineTime());
        return lqw;
    }

    /**
     * 新增设备管理-设备配置信息
     */
    @Override
    public Boolean insertByBo(DeviceConfigBo bo) {
        DeviceConfig add = MapstructUtils.convert(bo, DeviceConfig.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setDeviceId(add.getDeviceId());
        }
        return flag;
    }

    /**
     * 修改设备管理-设备配置信息
     */
    @Override
    public Boolean updateByBo(DeviceConfigBo bo) {
        return TenantHelper.ignore(() -> {
            DeviceConfig update = MapstructUtils.convert(bo, DeviceConfig.class);
            assert update != null;
            validEntityBeforeUpdate(update);

            return baseMapper.updateById(update) > 0;
        });
    }

    private void validEntityBeforeUpdate(DeviceConfig entity) {
        // 如果存在门店，并且修改了租户， 那么不能修改租户
        if (null == entity.getStoreId()) {
            return;
        }
        DeviceConfig deviceConfig = baseMapper.selectById(entity.getDeviceId());
        if (ObjUtil.isNull(deviceConfig)) {
            throw new ServiceException("请选择数据");
        }
        String tenantId = deviceConfig.getTenantId();
        if (!entity.getTenantId().equals(tenantId)) {
            throw new ServiceException("设备已经绑定门店，不能修改租户");
        }
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(DeviceConfig entity){
        if (StrUtil.isBlank(entity.getDeviceSn())) {
            throw new ServiceException("请输入设备编码");
        }

        // 判断这个设备是否已被添加
        List<DeviceConfig> deviceConfigs = TenantHelper.ignore(()->
                baseMapper.selectList(new LambdaQueryWrapper<DeviceConfig>()
                .eq(DeviceConfig::getDeviceSn, entity.getDeviceSn())));

        if (CollUtil.isNotEmpty(deviceConfigs)) {
            throw new ServiceException("请先解绑该设备");
        }
    }

    /**
     * 批量删除设备管理-设备配置信息
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        return TenantHelper.ignore(() -> {
            if (isValid) {
                if (checkDeviceExistStore(ids)) {
                    throw new ServiceException("存在门店，不允许删除");
                }

            }
            return baseMapper.deleteBatchIds(ids) > 0;
        });
    }

    @Override
    public TableDataInfo<DeviceVo> queryTableList(DeviceBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<DeviceConfig> lqw = buildQueryWrappers(bo);
        IPage<DeviceConfigVo> table = baseMapper.selectVoPage(pageQuery.build(), lqw);
        List<DeviceConfigVo> records = table.getRecords();
        if (CollUtil.isEmpty(records)) {
            return TableDataInfo.build();
        }

        buildStoreName(records);
        List<DeviceVo> deviceVos = BeanUtil.copyToList(records, DeviceVo.class);
        return TableDataInfo.build(deviceVos);
    }

    @Override
    public int bindDevice(DeviceBo bo) {
        DeviceConfig deviceConfig = baseMapper.selectOne(new LambdaQueryWrapper<>(DeviceConfig.class)
                .eq(DeviceConfig::getDeviceSn, bo.getDeviceSn()));
        if (ObjUtil.isNull(deviceConfig)) {
            log.info("设备数据：【{}】不存在", bo.getDeviceSn());
            throw new ServiceException("设备不存在");
        }
        if (deviceConfig.getStoreId() != null) {
            log.info("设备数据：【{}】已绑定门店", bo.getDeviceSn());
            throw new ServiceException("设备已绑定门店");
        }

        return insertStoreArea(bo, deviceConfig);
    }

    /**
     * 构建门店分区对象
     * @param storeAreaId       门店分区ID
     * @param deviceId          设备ID
     * @param list              空的分区对象列表
     */
    private void buildStoreArea(String storeAreaId, Long deviceId, List<DeviceStoreArea> list) {
        DeviceStoreArea dsa = new DeviceStoreArea();
        dsa.setDeviceId(deviceId);
        dsa.setStoreAreaId(Long.parseLong(storeAreaId));
        list.add(dsa);
    }

    @Override
    public int updateBindBo(DeviceBo bo) {
        DeviceConfig deviceConfig = baseMapper.selectById(bo.getDeviceId());
        if (ObjUtil.isNull(deviceConfig)) {
            log.info("设备数据：【{}】不存在", bo.getDeviceSn());
            throw new ServiceException("请选择数据");
        }
        // 删除DeviceStoreArea对象
        deviceStoreAreaMapper.delete(new LambdaQueryWrapper<DeviceStoreArea>()
                .eq(DeviceStoreArea::getDeviceId, bo.getDeviceId()));

        return insertStoreArea(bo, deviceConfig);
    }

    @Override
    @GlobalTransactional
    public int remove(List<Long> deviceIds) {
        return TenantHelper.ignore(() -> {
            // 门店与分区 删除DeviceStoreArea关系对象
            deviceStoreAreaMapper.delete(new LambdaQueryWrapper<DeviceStoreArea>()
                    .in(DeviceStoreArea::getDeviceId, deviceIds));

            baseMapper.update(
                    Wrappers.<DeviceConfig>lambdaUpdate()
                            .set(DeviceConfig::getStoreId, null)
                            .in(DeviceConfig::getDeviceId, deviceIds)
            );
            // 远程删除设备与用户之间的关系
            remoteUserService.deleteUserAndDevice(deviceIds);

            return 1;
        });
    }

    @Override
    public void updateBatchStatusByDeviceSn(List<DeviceConfig> deviceVos) {
        if (CollUtil.isNotEmpty(deviceVos) && deviceVos.size() > 5000) {
            throw new ServiceException("批量的更新数据过大");
        }
        List<String> deviceSnList = deviceVos.stream().map(DeviceConfig::getDeviceSn).distinct().toList();
        List<DeviceConfig> deviceConfigs = baseMapper.selectList(new LambdaQueryWrapper<DeviceConfig>()
                .in(DeviceConfig::getDeviceSn, deviceSnList));
        Map<String, String> map = StreamUtils.toMap(deviceVos, DeviceConfig::getDeviceSn, DeviceConfig::getStatus);

        List<DeviceConfig> list = new ArrayList<>();
        deviceConfigs.forEach(device-> {
            String s = map.get(device.getDeviceSn());
            if (StrUtil.isNotBlank(s)) {
                DeviceConfig deviceConfig = new DeviceConfig();
                deviceConfig.setDeviceId(device.getDeviceId());
                deviceConfig.setDeviceSn(device.getDeviceSn());
                deviceConfig.setStoreId(device.getStoreId());
                deviceConfig.setStatus(s);
                list.add(deviceConfig);
            }

        });

        baseMapper.updateBatchById(list);
    }

    @Override
    public List<DeviceConfigVo> selectOnlineByDeviceSnList(String[] deviceSns) {

        return baseMapper.selectVoList(new LambdaQueryWrapper<DeviceConfig>()
                .in(DeviceConfig::getDeviceSn, Arrays.asList(deviceSns))
                .eq(DeviceConfig::getStatus, DeviceConfigBo.ONLINE)
        );
    }

    @Override
    public List<DeviceConfigVo> selectByDeviceIds(List<Long> deviceIds) {
        return baseMapper.selectVoBatchIds(deviceIds);
    }

    @Override
    public List<OrdinaryDeviceVo> queryByStoreIds(List<Long> storeIds) {
        if (CollUtil.isEmpty(storeIds)) {
            return Collections.emptyList();
        }
        List<DeviceConfigVo> vos = baseMapper.selectVoList(new LambdaQueryWrapper<>(DeviceConfig.class)
                .in(DeviceConfig::getStoreId, storeIds));
        if (CollUtil.isEmpty(vos)) {
            return Collections.emptyList();
        }
        return BeanUtil.copyToList(vos, OrdinaryDeviceVo.class);
    }

    @Override
    public List<DeviceInfoVo> groupStore(Page<DeviceInfoVo> page, DeviceInfoBo bo) {
        String storeIds = bo.getStoreIds();
        if (StrUtil.isNotBlank(storeIds)) {
            List<Long> storeIdList = StrUtil.split(storeIds, ",").stream().mapToLong(Long::parseLong).boxed()
                    .collect(Collectors.toList());
            bo.setStoreIdList(storeIdList);
        }
        List<DeviceInfoVo> deviceInfoVos = baseMapper.groupStore(page, bo);
        if (CollUtil.isEmpty(deviceInfoVos)) {
            return Collections.emptyList();
        }
        List<Long> storeIdList = deviceInfoVos.stream().map(DeviceInfoVo::getStoreId)
                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
        Map<Long, String> storeMap = remoteStoreService.selectNamesByStoreList(storeIdList);
        if (MapUtil.isEmpty(storeMap)) {
            return deviceInfoVos;
        }
        deviceInfoVos.forEach(deviceInfoVo -> {
            deviceInfoVo.setStoreName(storeMap.get(deviceInfoVo.getStoreId()));
        });

        return deviceInfoVos;
    }

    @Override
    public List<DeviceConfigVo> selectByNotStoreId() {
        return this.baseMapper.selectVoList(new LambdaQueryWrapper<DeviceConfig>().isNotNull(DeviceConfig::getStoreId));
    }

    @Override
    public boolean unbindByStoreId(Long storeId) {
        int update = this.baseMapper.update(new LambdaUpdateWrapper<DeviceConfig>()
                .set(DeviceConfig::getStoreId, null)
                .eq(DeviceConfig::getStoreId, storeId)
        );

        return update > 0;
    }

    /**
     * 保存设备与门店的关系
     * @param bo                分区对象ID
     * @param deviceConfig      设备对象
     */
    private int insertStoreArea(DeviceBo bo, DeviceConfig deviceConfig) {
        String storeAreaIds = bo.getStoreAreaIds();
        if (StrUtil.isNotBlank(storeAreaIds)) {
            List<String> storeAreaList = StrUtil.split(storeAreaIds, ",");
            List<DeviceStoreArea> list = new ArrayList<>(storeAreaList.size());
            storeAreaList.forEach(sa -> buildStoreArea(sa, deviceConfig.getDeviceId(), list));
            deviceStoreAreaMapper.insertBatch(list);

        }
        deviceConfig.setDeviceArea(bo.getDeviceArea());
        deviceConfig.setDisplay(bo.getDisplay());
        deviceConfig.setName(bo.getName());
        deviceConfig.setLoginKey(bo.getLoginKey());
        deviceConfig.setStoreId(bo.getStoreId());
        deviceConfig.setBingTime(TimeUtil.toMilliseconds(LocalDateTime.now()));
        return baseMapper.updateById(deviceConfig);
    }

    private LambdaQueryWrapper<DeviceConfig> buildQueryWrappers(DeviceBo bo) {
        LambdaQueryWrapper<DeviceConfig> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getDeviceSn()), DeviceConfig::getDeviceSn, bo.getDeviceSn());
        lqw.like(StringUtils.isNotBlank(bo.getName()), DeviceConfig::getName, bo.getName());

        lqw.eq(StringUtils.isNotBlank(bo.getDisplay()), DeviceConfig::getDisplay, bo.getDisplay());
        lqw.eq(StringUtils.isNotBlank(bo.getControl()), DeviceConfig::getControl, bo.getControl());
        lqw.eq(bo.getStoreId() != null, DeviceConfig::getStoreId, bo.getStoreId());
        lqw.in(ObjUtil.isNotNull(bo.getStoreIds()), DeviceConfig::getStoreId, bo.getStoreIds());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), DeviceConfig::getStatus, bo.getStatus());
        lqw.orderByDesc(DeviceConfig::getBingTime);


        lqw.apply(" store_id is not null");
        return lqw;
    }

    /**
     * 判断设备是否存在门店
     * @param deviceIds     设备ID集合
     */
    private boolean checkDeviceExistStore(Collection<Long> deviceIds) {
        List<DeviceConfigVo> vos = baseMapper.selectVoBatchIds(deviceIds);
        if (CollUtil.isEmpty(vos)) {
            return Boolean.FALSE;
        }
        long count = vos.stream().filter(x -> ObjUtil.isNotNull(x.getStoreId())).count();
        return count > 0;
    }
}
